package cplexSolve;

import java.util.ArrayList;

import huq.com.common.Conf;
import huq.com.common.Lane;
import huq.com.common.Instance;
import huq.com.common.Solution;
import huq.com.common.Carrier;
import ilog.concert.IloIntVar;
import ilog.concert.IloLinearNumExpr;
import ilog.cplex.IloCplex;
/**
 * cherry picking
 * @author User
 *
 */
public class F {
	Instance inst;
	int I;
	int J;
	int[] b;
	int[][] c;
	int[][] r;
	int[][] z;

	int H;
	int[] L;
	int[] U;
	int[] D;
	
	public F() {
		// TODO Auto-generated constructor stub
		H = 5;
		L = new int[H];
		U = new int[H];
		D = new int[H];
		
		L[0] = 0;
		U[0] = L[1] = 100;
		U[1] = L[2] = 300;
		U[2] = L[3] = 500;
		U[3] = L[4] = 1000;
		U[4] = Integer.MAX_VALUE;
		
		D[0] = 1;
		D[1] = 2;
		D[2] = 3;
		D[3] = 4;
		D[4] = 5;
	}
	
	public F(Instance inst) {
		// TODO Auto-generated constructor stub
		this();
		this.inst = inst;
		this.I = inst.I;
		this.J = inst.J;
		
		b = new int[I];
		c = new int[I][J];
		r = new int[I][J];
		
		for(int i = 0; i < I; i++){
			b[i] = inst.shipper.shipments[i];
			for(int j = 0; j < J; j++){
				Carrier carrier = inst.carriers.get(j);
				if(carrier.lanes[i] == null) continue;
				c[i][j] = carrier.caps[i];
				r[i][j] = carrier.rates[i];
			}
		}

		z = new int[I][H];
		for(int i = 0; i < I; i++){
			for(int h = 0; h < H; h++){
				if(L[h] <= b[i] && b[i] < U[h]){
					z[i][h] = 1;
				}
			}
		}
	}
	
	public Solution solve(int timeLimit) throws Exception{
		/**
		 * with cplex to solve the Model 
		 * 
		 */
		IloIntVar[][] x = new IloIntVar[I][J];	// item
		IloIntVar[][] y = new IloIntVar[I][J];
		
		IloCplex cplex = new IloCplex();
		cplex.setParam(IloCplex.DoubleParam.TiLim, timeLimit);	// set time limit
		cplex.setParam(IloCplex.IntParam.MIPEmphasis, IloCplex.MIPEmphasis.BestBound);	// choose the way
		cplex.setParam(IloCplex.IntParam.NodeFileInd, 2);
		cplex.setParam(IloCplex.DoubleParam.TreLim, 12000);
		cplex.setOut(null);
			
		// 1. bounds
		for(int j = 0; j < J; j++){
			for(int i = 0; i < I; i++){
				x[i][j] = cplex.intVar(0, c[i][j], "x_" + i + "_" + j);
				y[i][j] = cplex.boolVar("y_" + j);
			}
		}
		
		// 2. objective
		IloLinearNumExpr expr = cplex.linearNumExpr();
		for(int i = 0; i < I; i++){
			for(int j = 0; j < J; j++){
				expr.addTerm(r[i][j], x[i][j]);
			}
		}
		cplex.addMinimize(expr);

		// 3. subject to
		for(int i = 0; i < I; i++){
			IloLinearNumExpr exp1 = cplex.linearNumExpr();
			for(int j = 0; j < J; j++){
				exp1.addTerm(1, x[i][j]);
			}
			cplex.addGe(exp1, b[i]);
		}

		for(int i = 0; i < I; i++){
			for(int h = 0; h < H; h++){
				IloLinearNumExpr exp1 = cplex.linearNumExpr();
				for(int j = 0; j < J; j++){
					exp1.addTerm(1, y[i][j]);
				}
				cplex.addGe(exp1, D[h] * z[i][h]);
			}
		}
		
		for(int i = 0; i < I; i++){
			for(int j = 0; j < J; j++){
				IloLinearNumExpr exp1 = cplex.linearNumExpr();
				IloLinearNumExpr exp2 = cplex.linearNumExpr();
				exp1.addTerm(1, x[i][j]);
				exp2.addTerm(Integer.MAX_VALUE, y[i][j]);
				cplex.addLe(exp1, exp2);
			}
		}
		
		for(int i = 0; i < I; i++){
			for(int j = 0; j < J; j++){
				IloLinearNumExpr exp1 = cplex.linearNumExpr();
				IloLinearNumExpr exp2 = cplex.linearNumExpr();
				exp1.addTerm(1, y[i][j]);
				exp2.addTerm(1, x[i][j]);
				cplex.addLe(exp1, exp2);
			}
		}
		
		// 4. export Model
		 cplex.exportModel("Model_F.lp");
		// 5. solve	
		if(!cplex.solve()){
			System.out.println("Code 101: Cannot be solved!");
			return null;
		}
		if(cplex.getStatus()==IloCplex.Status.Infeasible){
			System.out.println("Code 102: Infeasible");
			return null;
		}
		else if(cplex.getStatus()==IloCplex.Status.Feasible || cplex.getStatus() == IloCplex.Status.Optimal){
			Solution solution = new Solution();
			solution.totalCost = cplex.getObjValue();
			solution.time = cplex.getCplexTime();
			double lb = cplex.getBestObjValue();
			solution.gap = 1.0*(solution.totalCost - lb)/lb;
			if(cplex.getStatus() == IloCplex.Status.Optimal){
				solution.status = "Optimal";
			}
			else{
				solution.status = "Feasible";
			}
			System.out.println(solution.status);
			// get Solution
			int [][] X=new int[I][J];
			for(int i=0;i<I;i++){
				Lane lane = inst.lanes.get(i);
				ArrayList<Carrier> carriers = new ArrayList<Carrier>();
				ArrayList<Integer> shipments = new ArrayList<Integer>();
				for(int j=0;j<J;j++){
					double d = cplex.getValue(x[i][j]);
					int b=(int)Math.round(d);
					if(Math.abs(d-b)>Conf.RC_EPS){
						System.out.println("Not an integer!");
					}
					if(b > 0){
						X[i][j] = b;
						carriers.add(inst.carriers.get(j));
						shipments.add(b);
					}
				}
				solution.addLaneSolution(lane, carriers, shipments);
			}
			cplex.end();
			return solution;
		}
		else if(cplex.getCplexTime()>timeLimit-1){
			System.out.println("Code 104: Time Out!");
			return null;
		}
		return null;
	}
}
